package com.magoldeditor.util.xml;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * 
 * @author Velconia
 * @version v.1.0
 * 
 */
public abstract class MaEditorXMLWriter implements IXMLWriter{
	
	/**
	 * name
	 */
	private static final String NAME = "MaEditorXMLWriter";

	/**
	 * encoding
	 */
	private static final String ENCODING = "gb2312";
	
	/**
	 * document
	 */
	Document document = null;
	
	/**
	 * last node
	 */
	Element lastNode = null;
	
	/**
	 * output stream
	 */
	private OutputStreamWriter writer = null;
	
	/**
	 * 
	 * @param fileName
	 * @throws FileNotFoundException
	 */
	public MaEditorXMLWriter(final String fileName) throws FileNotFoundException {
		this(new File(fileName));
	}
	
	/**
	 * 
	 * @param file
	 * @throws FileNotFoundException
	 */
	public MaEditorXMLWriter(final File file) throws FileNotFoundException {
		this(new FileOutputStream(file));
	}
	
	/**
	 * 
	 * @param fileOutputStream
	 */
	public MaEditorXMLWriter(final FileOutputStream fileOutputStream) {
		try {
			init(fileOutputStream);
			if (check()) return;
			else
				throw new RuntimeException(NAME + "initialized failly");
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Node setRoot(String element) {
		Node n = this.document.createElement(element);
		this.lastNode = (Element) n;
		if (!this.document.hasChildNodes()) return this.document.appendChild(n);
		else throw new RuntimeException("you should not setRoot again");
	}

	@Override
	public Node appendChild(Node parent, String child) {
		Element e = this.document.createElement(child);
		parent.appendChild(e);
		this.lastNode = e;
		return e;
	}

	@Override
	public void setAttribute(String key, String value) {
		if (lastNode == null) {
			throw new RuntimeException("you should init the node first");
		} else {
			this.lastNode.setAttribute(key, value);
		}
	}
	
	@Override
	public void setAttribute(Node n, String key, String value) {
		Element e = (Element) n;
		e.setAttribute(key, value);
	}
	
	@Override
	public void write() throws IOException {
		if (check()) {
			callWriteXmlFile(this.document, this.writer, ENCODING);
			this.writer.close();
		} else throw new RuntimeException("you should init the " + NAME + " first");
	}
	
	@Override
	public void finalize() {
		this.writer = null;
		this.document = null;
		this.lastNode = null;
	}
	
	@Override
	public String toString() {
		return this.getClass().getSimpleName();
	}
	
	private void init(final FileOutputStream fileOutputStream) throws ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		this.document = db.newDocument();
		
		this.writer = new OutputStreamWriter(fileOutputStream);
	}
	
	private void callWriteXmlFile(Document doc, Writer w, String encoding) {
		try {
		   Source source = new DOMSource(doc);
		   Result result = new StreamResult(w);
		   
		   Transformer xformer = TransformerFactory.newInstance().newTransformer();
		   xformer.setOutputProperty(OutputKeys.ENCODING, encoding);
		   xformer.transform(source, result);
		   
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}
	
	private boolean check() {
		if (this.writer == null || this.document == null) return false;
		else return true;
	}
	
	public OutputStreamWriter getWriter() {
		return writer;
	}

	public void setWriter(OutputStreamWriter writer) {
		this.writer = writer;
	}
}
